﻿/* ######################################## *\
 * ### Copyright (C) 2009 AJ Ravindiran ### *
 * ### HTTP://THEAJ.NET/ <AJ@THEAJ.NET> ### *
\* ######################################## */
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Generic;

namespace AJRavindiran.Jolt.Network
{
    public class TCPConnection
    {
        #region Fields
        private Socket mSocket;
        private int mPort;
        private int mMaxConnections;
        private int mAcceptedConnections;
        private HashSet<int> mActiveConnections;
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the max ammount of connections availible at the same time.
        /// </summary>
        public int MaxConncetions
        {
            get { return mMaxConnections; }
            set { mMaxConnections = value; }
        }

        /// <summary>
        /// Gets or sets the ammount of accepted connections since startup.
        /// </summary>
        public int AcceptedConnections
        {
            get { return mAcceptedConnections; }
            set { mAcceptedConnections = value; }
        }

        /// <summary>
        /// Gets an IP Adress.
        /// </summary>
        public string IPAdress
        {
            get
            {
                if (mSocket == null)
                {
                    return "0.0.0.0";
                }

                return mSocket.RemoteEndPoint.ToString().Split(':')[0];
            }
        }

        /// <summary>
        /// Gets if the socket server is still alive.
        /// </summary>
        public bool Alive
        {
            get { return (mSocket != null); }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Constructs a new TCPConnection class. Assigns values to
        /// <code>mPort</code> and <code>mMaxConnection</code>
        /// </summary>
        /// <param name="port">The port the TCP connection will be ran on</param>
        /// <param name="maxConnections">The maximum ammount of connections availible on the server at once.</param>
        public TCPConnection(int port, int maxConnections)
        {
            this.mPort = port;
            this.mMaxConnections = maxConnections;
            JoltEnvironment.GetLogger().WriteInfo("TCP Connection is ready to be initialized.");
        }
        #endregion

        #region Methods
        /// <summary> 
        /// Initializes the socket listener for game connections and starts listening. 
        /// </summary> 
        /// <param name="mPort">The port where the socket listener should be bound to.</param> 
        /// <param name="mMaxConnections">The maximum amount of simultaneous connections.</param> 
        /// <remarks></remarks> 
        public bool Initialize()
        {
            this.mActiveConnections = new HashSet<int>();

            mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                mSocket.Bind(new IPEndPoint(IPAddress.Any, mPort));
                mSocket.Listen(25);
                mSocket.BeginAccept(new AsyncCallback(connectionRequest), mSocket);

                JoltEnvironment.GetLogger().WriteClean("Successfully opened TCP connection on port #" + mPort);
                return true;
            }
            catch (Exception ex)
            {
                JoltEnvironment.GetLogger().WriteException(ex);
                return false;
            }
        }

        /// <summary>
        /// Connection request. Connects players who are quenched.
        /// </summary>
        private void connectionRequest(IAsyncResult iAr)
        {
            int connectionID = 0;
            for (int i = 1; i < mMaxConnections; i++)
            {
                if (mActiveConnections.Contains(i) == false)
                {
                    connectionID = i;
                    break;
                }
            }

            if (connectionID > 0)
            {
                Socket connectionSocket = ((Socket)iAr.AsyncState).EndAccept(iAr);
                JoltEnvironment.GetLogger().WriteInfo("Accepted connection [" + connectionID + "] from " + connectionSocket.RemoteEndPoint.ToString().Split(':')[0]);
                mActiveConnections.Add(connectionID);
                mAcceptedConnections++;

                // virtualUser newUser = new virtualUser(connectionID, connectionSocket);
                try
                {
                    RuneScape.RuneScape.GetEngine().GetCharacterManager().AddCharacter(connectionSocket, connectionID);
                }
                catch (Exception e) { JoltEnvironment.GetLogger().WriteException(e); }
            }
        }

        /// <summary>
        /// Flags a connection as free. 
        /// </summary>
        /// <param name="connectionID">The ID of the connection.</param>
        public void FreeConnection(int connectionID)
        {
            // If the active connections hashset contains the
            // connectionid requested, it will close the connection.
            if (mActiveConnections.Contains(connectionID))
            {
                mActiveConnections.Remove(connectionID);
                JoltEnvironment.GetLogger().WriteClean("Flagged connection [" + connectionID + "] as free.");
            }
        }

        /// <summary>
        /// Shutdown the TCP Connection.
        /// </summary>
        public void ShutDown()
        {
            // If the TCP connection is already dead, do nothing.
            if (!Alive)
                return;

            // Close and null the connection so we don't waste memory.
            mSocket.Close();
            mSocket = null;
        }
        #endregion
    }
}
